//! Core traits for working with execution payloads.

use alloc::vec::Vec;
use alloy_eips::{
    eip4895::{Withdrawal, Withdrawals},
    eip7685::Requests,
};
use alloy_primitives::{Address, B256, U256};
use alloy_rpc_types_engine::{PayloadAttributes as EthPayloadAttributes, PayloadId};
use core::fmt;
use reth_chain_state::ExecutedBlockWithTrieUpdates;
use reth_primitives_traits::{NodePrimitives, SealedBlock};

/// Represents a successfully built execution payload (block).
///
/// Provides access to the underlying block data, execution results, and associated metadata
/// for payloads ready for execution or propagation.
#[auto_impl::auto_impl(&, Arc)]
pub trait BuiltPayload: Send + Sync + fmt::Debug {
    /// The node's primitive types
    type Primitives: NodePrimitives;

    /// Returns the built block in its sealed (hash-verified) form.
    fn block(&self) -> &SealedBlock<<Self::Primitives as NodePrimitives>::Block>;

    /// Returns the total fees collected from all transactions in this block.
    fn fees(&self) -> U256;

    /// Returns the complete execution result including state updates.
    ///
    /// Returns `None` if execution data is not available or not tracked.
    fn executed_block(&self) -> Option<ExecutedBlockWithTrieUpdates<Self::Primitives>> {
        None
    }

    /// Returns the EIP-7685 execution layer requests included in this block.
    ///
    /// These are requests generated by the execution layer that need to be
    /// processed by the consensus layer (e.g., validator deposits, withdrawals).
    fn requests(&self) -> Option<Requests>;
}

/// Attributes used to guide the construction of a new execution payload.
///
/// Extends basic payload attributes with additional context needed during the
/// building process, tracking in-progress payload jobs and their parameters.
pub trait PayloadBuilderAttributes: Send + Sync + fmt::Debug {
    /// The external payload attributes format this type can be constructed from.
    type RpcPayloadAttributes;
    /// The error type used in [`PayloadBuilderAttributes::try_new`].
    type Error: core::error::Error;

    /// Constructs new builder attributes from external payload attributes.
    ///
    /// Validates attributes and generates a unique [`PayloadId`] based on the
    /// parent block, attributes, and version.
    fn try_new(
        parent: B256,
        rpc_payload_attributes: Self::RpcPayloadAttributes,
        version: u8,
    ) -> Result<Self, Self::Error>
    where
        Self: Sized;

    /// Returns the unique identifier for this payload build job.
    fn payload_id(&self) -> PayloadId;

    /// Returns the hash of the parent block this payload builds on.
    fn parent(&self) -> B256;

    /// Returns the timestamp to be used in the payload's header.
    fn timestamp(&self) -> u64;

    /// Returns the beacon chain block root from the parent block.
    ///
    /// Returns `None` for pre-merge blocks or non-beacon contexts.
    fn parent_beacon_block_root(&self) -> Option<B256>;

    /// Returns the address that should receive transaction fees.
    fn suggested_fee_recipient(&self) -> Address;

    /// Returns the randomness value for this block.
    fn prev_randao(&self) -> B256;

    /// Returns the list of withdrawals to be processed in this block.
    fn withdrawals(&self) -> &Withdrawals;
}

/// Basic attributes required to initiate payload construction.
///
/// Defines minimal parameters needed to build a new execution payload.
/// Implementations must be serializable for transmission.
pub trait PayloadAttributes:
    serde::de::DeserializeOwned + serde::Serialize + fmt::Debug + Clone + Send + Sync + 'static
{
    /// Returns the timestamp for the new payload.
    fn timestamp(&self) -> u64;

    /// Returns the withdrawals to be included in the payload.
    ///
    /// `Some` for post-Shanghai blocks, `None` for earlier blocks.
    fn withdrawals(&self) -> Option<&Vec<Withdrawal>>;

    /// Returns the parent beacon block root.
    ///
    /// `Some` for post-merge blocks, `None` for pre-merge blocks.
    fn parent_beacon_block_root(&self) -> Option<B256>;
}

impl PayloadAttributes for EthPayloadAttributes {
    fn timestamp(&self) -> u64 {
        self.timestamp
    }

    fn withdrawals(&self) -> Option<&Vec<Withdrawal>> {
        self.withdrawals.as_ref()
    }

    fn parent_beacon_block_root(&self) -> Option<B256> {
        self.parent_beacon_block_root
    }
}

#[cfg(feature = "op")]
impl PayloadAttributes for op_alloy_rpc_types_engine::OpPayloadAttributes {
    fn timestamp(&self) -> u64 {
        self.payload_attributes.timestamp
    }

    fn withdrawals(&self) -> Option<&Vec<Withdrawal>> {
        self.payload_attributes.withdrawals.as_ref()
    }

    fn parent_beacon_block_root(&self) -> Option<B256> {
        self.payload_attributes.parent_beacon_block_root
    }
}

/// Factory trait for creating payload attributes.
///
/// Enables different strategies for generating payload attributes based on
/// contextual information. Useful for testing and specialized building.
pub trait PayloadAttributesBuilder<Attributes>: Send + Sync + 'static {
    /// Constructs new payload attributes for the given timestamp.
    fn build(&self, timestamp: u64) -> Attributes;
}
